Este glossário descreve os principais objetos, dataframes e ficheiros criados ao longo deste projeto de análise e otimização de carteiras.
Ir para a seção de Coleta de Dados
tickers_rv (Vetor de texto):
dados_yf (Dataframe):
date e os tickers de cada ativo (ex: VALE3,
PETR4). Gerado pela função
baixar_dados_yf.dados_btc_brl (Dataframe):
baixar_btc_em_reais.df_fator_cdi (Dataframe):
1 + a taxa de juro diária. Gerado pela função
baixar_cdi_direto.Ir para a seção de Transformação de dados
df_retornos (Dataframe):
(hoje/ontem) - 1) para todos os ativos. É usado para
calcular o backtest do desempenho das carteiras.df_log_retornos (Dataframe):
df_retornos_finais. Contém os retornos
logarítmicos (log(1 + retorno_percentual)) para
todos os ativos. Este formato é matematicamente preferível para análises
estatísticas e é o input principal o Índice de Sharpe e para a
otimização de Markowitz.Ir para a seção de Construção da Carteira
indices_sharpe_log (Dataframe):
df_top_sharpe (Dataframe):
df_log_retornos, contendo apenas os ativos com o melhor
desempenho histórico segundo o Índice de Sharpe. Representa o nosso
universo de ativos “pré-selecionados” para a otimização.carteira_otima_5_risco (Objeto R):
optimize.portfolio. Ele contém todas as informações
sobre a carteira otimizada, incluindo o mais importante: o vetor de
pesos.pesos_otimizados:
Ir para a seção de resultados e publicação
pesos_otimizados.rds (Ficheiro de dados
R):
precos_atuais.rds (Ficheiro de dados
R):
app.R (Script Shiny):
.rds e o
relatorio.html para criar a interface web com as duas
páginas.publish.yml (Ficheiro de
configuração):
#Aqui definimos o corte de dados que incluiremos no DataFrame.
data_inicio <- as.Date("2016-01-04")
data_fim <- as.Date("2025-09-30")
#Lista dos tickers pesquisados, top16 do IBOV + ETFs de SP500 e IBOV
tickers_rv<- c(
"IVVB11.SA",
"BOVA11.SA",
"VALE3.SA",
"ITUB4.SA",
"PETR4.SA",
"ELET3.SA",
"BBDC4.SA",
"SBSP3.SA",
"B3SA3.SA",
"BPAC11.SA",
"ITSA4.SA",
"BBAS3.SA",
"EMBR3.SA",
"WEGE3.SA",
"ABEV3.SA",
"EQTL3.SA",
"RDOR3.SA",
"RENT3.SA"
)
Para nossos ativos de renda variável, tomaremos os índices IBOV, SP500, para manter possível a montagem de uma carteira do investidor, utilizaremos um ETF correspondente a cada um dos índices. Incluiremos ainda as top 16 ações com mais peso no IBOV, uma vez que são empresas consolidadas e que tem liquidez suficiente para montar grandes posições.
Para coleta dos preços de Renda variável usaremos a API do Yahoo Finance, que agrega dados de mercados de ativos de mundo todo
baixar_dados_yf <- function(tickers, data_inicio, data_fim) {
# Usaremos tq_get para baixar todos os dados brutos de uma só vez
dados_brutos <- tq_get(tickers,
get = "stock.prices",
from = data_inicio,
to = data_fim) %>%
select(symbol, date, close)
#Aproveitando, trocaremos os nomes dos tickers de volta para o padrão B3.
dados_limpos <- dados_brutos %>%
mutate(symbol = sub("\\.SA$", "", symbol))
# Montando nosso dataframe:
resultado_yf <- dados_limpos %>%
select(date, symbol, close) %>%
pivot_wider(names_from = symbol, values_from = close) %>%
arrange(date)
return(resultado_yf)
}
dados_yf <- baixar_dados_yf(tickers = tickers_rv,
data_inicio = data_inicio,
data_fim = data_fim)
# Visualizar as últimas 5 linhas do dataframe resultante
print(tail(dados_yf, 5))
## # A tibble: 5 × 19
## date IVVB11 BOVA11 VALE3 ITUB4 PETR4 ELET3 BBDC4 SBSP3 B3SA3 BPAC11
## <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2025-09-23 395. 143. 57.7 39.0 31.9 50.7 17.9 132. 13.3 47.8
## 2 2025-09-24 396. 143. 57.9 38.8 32.6 50.7 17.7 131. 13.2 47.8
## 3 2025-09-25 398. 142. 58.2 38.5 32.4 50.4 17.6 129. 12.9 47.3
## 4 2025-09-26 398. 142. 57.1 38.7 32.2 50.7 17.7 128. 13.2 48.0
## 5 2025-09-29 398 143. 57.3 38.9 31.8 52.7 17.8 130. 13.4 48.8
## # ℹ 8 more variables: ITSA4 <dbl>, BBAS3 <dbl>, EMBR3 <dbl>, WEGE3 <dbl>,
## # ABEV3 <dbl>, EQTL3 <dbl>, RDOR3 <dbl>, RENT3 <dbl>
Para diversificação da nossa carteira, incluiremos também o BTC. No entanto ele é cotado em dólar então já contruiremos o df convertendo pelo câmbio de mercado.
baixar_btc_em_reais <- function(data_inicio, data_fim) {
# Dados do BTC
btc_usd <- tq_get("BTC-USD",
get = "stock.prices",
from = data_inicio,
to = data_fim) %>%
select(symbol, date, close)
cambio_brl <- tq_get("BRL=X",
get = "stock.prices",
from = data_inicio,
to = data_fim) %>%
select(date, cambio_taxa = close)
#Juntaremos os dataframes usando inner_join, dessa forma removeremos os dias onde o BTC é o cotado mas o mercardo brasileiro não opera, facilitando o passo futuro de juntar com o DF de ações.
dados_combinados <- inner_join(btc_usd, cambio_brl, by = "date")
#Convertando o preço de fechamento do BTC
btc_convertido <- dados_combinados %>%
mutate(
BTC = close * cambio_taxa,
) %>%
# Seleciona e reordena as colunas para o resultado final
select(date, BTC)
return(btc_convertido)
}
dados_btc_brl <- baixar_btc_em_reais(data_inicio, data_fim)
# Visualizar as últimas 5 linhas do resultado
print(tail(dados_btc_brl, 5))
## # A tibble: 5 × 2
## date BTC
## <date> <dbl>
## 1 2025-09-23 591616.
## 2 2025-09-24 604030.
## 3 2025-09-25 584908.
## 4 2025-09-28 598988.
## 5 2025-09-29 608805.
Por fim, o último ativo que comporá nosso portfolio será o CDI, que trataremos também como nossa taxa livre de risco, optamos por incluí-lô já que todo portfolio equilibrado tem uma mistura de ativos arriscados com a taxa livre de risco.
Para coletar os dados do CDI, acessaremos o SGS do Banco Central, faremos o caminho longo, acessando a URL completa pacote “JSONLITE” já que o “rbcb” estava apresentando comportamente errático durante os testes
baixar_cdi_direto <- function(data_inicio, data_fim) {
# Formatando as datas para dd/MM/yyyy
data_inicio_formatada <- format(data_inicio, "%d/%m/%Y")
data_fim_formatada <- format(data_fim, "%d/%m/%Y")
# Montaremos a URL da API manualmente pois tive alguns problemas usando o "rbcb"
url_api <- modify_url("https://api.bcb.gov.br/dados/serie/bcdata.sgs.11/dados",
query = list(formato = "json",
dataInicial = data_inicio_formatada,
dataFinal = data_fim_formatada))
# Usando jsonlite para baixar e converter os dados
dados_brutos <- fromJSON(url_api)
# Processando os dados
dados_cdi <- dados_brutos %>%
mutate(
TAXA_P = as.numeric(gsub(",", ".", valor)), # Troca vírgula por ponto
date = as.Date(data, format = "%d/%m/%Y")
) %>%
mutate(FATOR_DIARIO = 1 + (TAXA_P / 100)) %>%
select(date, FATOR_DIARIO)
return(dados_cdi)
}
dados_cdi <- baixar_cdi_direto(data_inicio, data_fim)
Agora, montaremos um dataframe com o retorno de todos os nossos ativos, para facilitar nossas etapas posteriores de análise.
#Primeiro, vamos usar as funções que criamos para construir nossas variáveis, primeiramente, aproveitaremos os resultados que usamos para testar as funções.
df_precos_rv <- dados_yf
df_precos_btc <- dados_btc_brl
df_fator_cdi <- dados_cdi
# Calculando os retornos percentuais:
# Para RV
df_retornos_rv <- df_precos_rv %>%
mutate(across(
.cols = -date,
.fns = ~(. / lag(.) - 1),
.names = "{.col}"
))
# Retorno para o BTC
df_retorno_btc <- df_precos_btc %>%
mutate(BTC = (BTC / lag(BTC) - 1))
# Retorno para o CDI
df_retorno_cdi <- df_fator_cdi %>%
mutate(CDI = FATOR_DIARIO - 1) %>%
select(date, CDI)
# Criamos uma lista com os 3 dataframes
lista_de_retornos <- list(df_retornos_rv, df_retorno_btc, df_retorno_cdi)
# Usamos 'reduce' com 'full-join' para juntar todos os dataframes da lista, sem eliminar as datas que tem NA para algum dos ativos, uma vez que tivemos IPOs depois da data_inicio, um "inner-join" reduziria demais nosso dataframe.
df_retornos <- reduce(lista_de_retornos, function(x, y) full_join(x, y, by = "date")) %>%
# Garantindo a ordenação por data
arrange(date) %>%
# Removendo a primeira linha
filter(row_number() > 1)
head(df_retornos)
## # A tibble: 6 × 21
## date IVVB11 BOVA11 VALE3 ITUB4 PETR4 ELET3 BBDC4
## <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2016-01-05 -0.00132 0.00195 -0.0134 0.00836 -0.0277 0.0221 0.00263
## 2 2016-01-06 -0.0108 -0.0165 -0.0735 -0.00395 -0.0419 -0.0180 -0.0142
## 3 2016-01-07 -0.0134 -0.0254 -0.0595 -0.0190 -0.0219 -0.0495 -0.0202
## 4 2016-01-08 -0.0123 -0.00329 -0.0339 0.000404 0.00160 -0.0270 -0.0196
## 5 2016-01-11 -0.00750 -0.0158 -0.0285 -0.0117 -0.0287 -0.00794 -0.0283
## 6 2016-01-12 0.00655 -0.00878 -0.0811 0.00449 -0.0920 0.0260 -0.00570
## # ℹ 13 more variables: SBSP3 <dbl>, B3SA3 <dbl>, BPAC11 <dbl>, ITSA4 <dbl>,
## # BBAS3 <dbl>, EMBR3 <dbl>, WEGE3 <dbl>, ABEV3 <dbl>, EQTL3 <dbl>,
## # RDOR3 <dbl>, RENT3 <dbl>, BTC <dbl>, CDI <dbl>
Enquanto os retornos são a forma ideal de vizualizar a rentabilidade de um portfólio, os log retornos permitem garantem uma distribuição que se aproxima da normal, e permitem uma abordagem aditiva, tendo como resultado estatísticas melhores. Dessa forma, para usaremos os log retornos para nossa etapa de análise técnica.
df_log_retornos <- df_retornos %>%
mutate(across(
.cols = -date, # Todas as colunas menos "date"
.fns = ~log(1 + .), #Log de 1 + retorno percentual
.names = "{.col}" # Mantém o mesmo nome da coluna
))
print(head(df_retornos))
## # A tibble: 6 × 21
## date IVVB11 BOVA11 VALE3 ITUB4 PETR4 ELET3 BBDC4
## <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2016-01-05 -0.00132 0.00195 -0.0134 0.00836 -0.0277 0.0221 0.00263
## 2 2016-01-06 -0.0108 -0.0165 -0.0735 -0.00395 -0.0419 -0.0180 -0.0142
## 3 2016-01-07 -0.0134 -0.0254 -0.0595 -0.0190 -0.0219 -0.0495 -0.0202
## 4 2016-01-08 -0.0123 -0.00329 -0.0339 0.000404 0.00160 -0.0270 -0.0196
## 5 2016-01-11 -0.00750 -0.0158 -0.0285 -0.0117 -0.0287 -0.00794 -0.0283
## 6 2016-01-12 0.00655 -0.00878 -0.0811 0.00449 -0.0920 0.0260 -0.00570
## # ℹ 13 more variables: SBSP3 <dbl>, B3SA3 <dbl>, BPAC11 <dbl>, ITSA4 <dbl>,
## # BBAS3 <dbl>, EMBR3 <dbl>, WEGE3 <dbl>, ABEV3 <dbl>, EQTL3 <dbl>,
## # RDOR3 <dbl>, RENT3 <dbl>, BTC <dbl>, CDI <dbl>
Para montar a carteira do investidor, primeiramente eliminaremos os ativos com os piores Índices de Sharpe. Como o CDI é a taxa livre de risco, não será possível calcular o Sharpe do mesmo e iremos incluí-lo como padrão, uma vez que a carteira ideal é aquela que equilibra investimentos de risco com o ativo livre de risco. Nesta primeira etapa plotaremos o gráfico dos Índices de Sharpe, os 15 ativos com os piores Sharpes serão eliminados para a próxima etapa.
#Primeiramente, calculamos o média do retorno livre de risco
log_retorno_medio_cdi <- mean(df_log_retornos$CDI, na.rm = TRUE)
# Calculando Sharpe dos outros ativos
indices_sharpe_log <- df_log_retornos %>%
# Removendo o CDI, já que não é possível calcular o Sharpe da taxa livre de risco
select(-CDI) %>%
pivot_longer(cols = -date, names_to = "ativo", values_to = "log_retorno") %>%
# Removendo NAs
na.omit() %>%
group_by(ativo) %>%
# Calculando os parâmetros:
summarise(
log_retorno_medio = mean(log_retorno),
desvio_padrao_log = sd(log_retorno)
) %>%
mutate(
# Fórmula do Sharpe Anualizado
sharpe_ratio_log = ((log_retorno_medio - log_retorno_medio_cdi)*252) / (desvio_padrao_log * sqrt(252))
)
#Separandos os ativos com os melhores Sharpes
top_4_ativos <- indices_sharpe_log %>%
# Ordena o dataframe em ordem decrescente
arrange(desc(sharpe_ratio_log)) %>%
# Pega as 9 primeiras linhas
slice_head(n = 4) %>%
# Extrai apenas a coluna 'ativo'
pull(ativo)
# Criamos um vetor com as colunas que manteremos
colunas_mant <- c("date", top_4_ativos, "CDI")
# Filtrando o dataframe
df_top_sharpe <- df_log_retornos %>%
select(all_of(colunas_mant))
head(df_top_sharpe)
## # A tibble: 6 × 6
## date BTC BPAC11 IVVB11 EQTL3 CDI
## <date> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2016-01-05 0.0195 NA -0.00132 0.0473 0.000525
## 2 2016-01-06 -0.00921 NA -0.0109 0.00576 0.000525
## 3 2016-01-07 0.0635 NA -0.0135 -0.0280 0.000525
## 4 2016-01-08 -0.00522 NA -0.0124 0.0135 0.000525
## 5 2016-01-11 -0.0227 NA -0.00753 -0.00175 0.000525
## 6 2016-01-12 -0.0146 NA 0.00653 0.00698 0.000525
#Gráfico de Barras dos Sharpes
grafico_estatico_log <- ggplot(indices_sharpe_log, aes(x = reorder(ativo, sharpe_ratio_log),
y = sharpe_ratio_log,
text = paste("Ativo:", ativo, "\nSharpe (Log):", round(sharpe_ratio_log, 2)))) +
geom_col(aes(fill = sharpe_ratio_log > 0)) +
coord_flip() +
scale_fill_manual(values = c("TRUE" = "darkgreen", "FALSE" = "orangered"), guide = "none") +
labs(
title = "Índice de Sharpe Anualizado (Baseado em Log Retornos)",
x = "Ativo",
y = "Índice de Sharpe (Log)"
) +
theme_minimal()
# Convertendo para interativo
grafico_interativo_log <- ggplotly(grafico_estatico_log, tooltip = "text") %>%
layout(showlegend = FALSE)
grafico_interativo_log
Agora encontraremos a carteira ótima, com os 4 ativos com o melhores Sharpes como mostra o gráfico acima + o CDI (taxa livre de risco). Definiremos um investimento Mínimo de 10% e máximo de 40% por ativo, para evitar exposição excessiva a ativos específicos.
# Primeiro converteremos para o formato "xts" que funciona melhor com os pacotes de análise financeira
retornos_xts_5 <- xts(df_top_sharpe[,-1], order.by = df_top_sharpe$date)
# Vetor de nomes
nomes_dos_5_ativos <- colnames(retornos_xts_5)
# Definindo o portfolio
port_spec_5_ativos <- portfolio.spec(assets = nomes_dos_5_ativos)
# Restrições:
#Gastar tudo
port_spec_5_ativos <- add.constraint(portfolio = port_spec_5_ativos, type = "full_investment")
#Sem "shorts"
port_spec_5_ativos <- add.constraint(portfolio = port_spec_5_ativos, type = "long_only")
#Diversificação
port_spec_5_ativos <- add.constraint(portfolio = port_spec_5_ativos,
type = "box",
min = 0.10,
max = 0.40)
# Maximizando o retorno
port_spec_5_ativos <- add.objective(portfolio = port_spec_5_ativos,
type = "return",
name = "mean")
#Minimizando o risco
port_spec_5_ativos <- add.objective(portfolio = port_spec_5_ativos,
type = "risk",
name = "StdDev")
#Otimização:
carteira_otima_5_risco <- optimize.portfolio(R = retornos_xts_5,
portfolio = port_spec_5_ativos,
optimize_method = "ROI",
trace = FALSE)
pesos_otimizados <- carteira_otima_5_risco$weights
Vamos rodar um backtest para comparar o desempenho da carteira ótima com uma carteira genérica que aloca igualmente nos 20 ativos. Criaremos nosso segundo gráfico comparando o retorno da nossa carteira otimizada com o retorno da carteira genérica com 20 ativos.
#Vetrores de nomes dos ativos que usaremos no tratamento das bases:
nomes_otimizados <- names(pesos_otimizados)
nomes_benchmark_20 <- setdiff(colnames(df_retornos), "date")
# DF do Backtest
df_retornos_backtest <- df_retornos %>%
filter(date >= as.Date("2022-01-01") & date <= as.Date("2025-09-30"))
# Retorno da carteira otimizada
retornos_otimizada <- as.matrix(df_retornos_backtest[, nomes_otimizados]) %*% pesos_otimizados
# Retornos da carteira genérica
retornos_peso_igual_20 <- rowMeans(df_retornos_backtest[, nomes_benchmark_20], na.rm = TRUE)
# Juntaremos tudo em um Dataframe para tratarmos os dados de forma unificada
df_retornos_carteiras <- tibble(
date = df_retornos_backtest$date,
Otimizada = as.numeric(retornos_otimizada),
`Peso Igual (20 Ativos)` = retornos_peso_igual_20
)
#Tratando o dataframe:
df_desempenho_acumulado <- df_retornos_carteiras %>%
# Pivota para o formato longo
pivot_longer(cols = -date, names_to = "Carteira", values_to = "Retorno_Diario") %>%
# Precisamos remover todos os NA e NaN pois na fórmula do "cumprod" estes gerarão um erro que será carregado
filter(!is.na(Retorno_Diario) & !is.nan(Retorno_Diario)) %>%
# Agrupamos por carteira
group_by(Carteira) %>%
# Calculamos o rendimento acumulado
mutate(Valor_Acumulado = cumprod(1 + Retorno_Diario)) %>%
ungroup()
# Montagem do gráfico de comparação
grafico_comparativo <- ggplot(df_desempenho_acumulado, aes(x = date, y = Valor_Acumulado, color = Carteira, group = Carteira)) +
geom_line(linewidth = 1) +
labs(
title = "Backtest: Carteira Otimizada vs. Carteira Genérica",
subtitle = "Crescimento de R$1,00 desde Jan/2022",
x = "Data",
y = "Valor Acumulado (R$)",
color = "Estratégia"
) +
scale_y_continuous(labels = scales::dollar_format(prefix = "R$")) +
scale_color_manual(values = c("Otimizada" = "navyblue", "Peso Igual (20 Ativos)" = "darkorange")) +
theme_minimal() +
theme(legend.position = "top")
ggplotly(grafico_comparativo)
O gráfico mostra que depois de um início um pouco mais fraco a carteira otimizada passou a apresentar retornos consideravelmente superiores que a carteira genérica
Vamos agora criar um dataframe de preços para calcularmos o portfolio do investidor dado um valor a ser investido.
#M criamos nosso dataframe de preços usando full join para manter todas as datas.
df_precos_todos <- full_join(dados_yf, dados_btc_brl, by = "date")
# Cotação mais recente:
precos_atuais_df <- df_precos_todos %>%
# Pivotamos para o formato longo para facilitar
pivot_longer(cols = -date, names_to = "ativo", values_to = "preco_atual") %>%
# Removemos os NA
na.omit() %>%
# Agrupamos por ativo para encontrar a cotação mais recente de cada
group_by(ativo) %>%
filter(date == max(date)) %>%
ungroup() %>%
select(ativo, preco_atual)
# Adicionamos o preço do CDI, que definiremos como 1, uma vez que podemos comprar qualquer valor em reais.
precos_atuais_df <- bind_rows(precos_atuais_df, tibble(ativo = "CDI", preco_atual = 1))
Abaixo temos uma demonstração de como é calculado o portfolio do investidor apartir de um valor investido hipotético, na outra aba do site, é possível executar esse mesmo processo para qualquer valor escolhido.
# Valor hipotético para demonstração apenas
valor_total_investimento <- 100000
#Calculando o valor ideal a ser investido em cada ativo
df_ordens <- tibble(
ativo = names(pesos_otimizados),
peso_otimizado = pesos_otimizados
) %>%
mutate(valor_alocado_ideal = peso_otimizado * valor_total_investimento) %>%
left_join(precos_atuais_df, by = "ativo")
# Valor real a ser investido
df_ordens <- df_ordens %>%
mutate(
# As ações e ETF são indivisíveis, então precisamos arredondar para baixo, BTC e CDI toleram receber qualquer quantia.
quantidade_a_comprar = case_when(
ativo == "BTC" ~ valor_alocado_ideal / preco_atual,
ativo != "CDI" ~ floor(valor_alocado_ideal / preco_atual),
TRUE ~ NA_real_
),
valor_real_alocado = case_when(
ativo != "BTC" & ativo != "CDI" ~ quantidade_a_comprar * preco_atual,
TRUE ~ valor_alocado_ideal
)
)
# Para não deixar sobrar caixa sem render, a sobra das ações será alocada no ativo livre de risco.
total_gasto_nao_cdi <- df_ordens %>%
filter(ativo != "CDI") %>%
summarise(total = sum(valor_real_alocado)) %>%
pull(total)
# Investimento no CDI é tudo que que falta pra completar o montante total
valor_final_cdi <- valor_total_investimento - total_gasto_nao_cdi
# Atualizamos o valor alocado no CDI
df_ordens <- df_ordens %>%
mutate(
valor_real_alocado = if_else(ativo == "CDI",
valor_final_cdi,
valor_real_alocado)
)
# Apresentando a lista de compras para nosso investidor:
lista_de_compras <- df_ordens %>%
select(
Ativo = ativo,
quantidade_a_comprar,
preco_atual,
valor_real_alocado
) %>%
# Formatando a resposta, considerando as divisibilidade do BTC e a característica monetária do CDI.
mutate(
`Quantidade / Fração` = case_when(
Ativo == "BTC" ~ formatC(quantidade_a_comprar, format = "f", digits = 8),
Ativo == "CDI" ~ scales::dollar(valor_real_alocado, prefix = "R$"),
TRUE ~ as.character(round(quantidade_a_comprar, 0))
),
`Preço Unitário (R$)` = if_else(Ativo == "CDI",
NA_character_,
as.character(round(preco_atual, 2))),
`Valor a Investir (R$)` = scales::dollar(valor_real_alocado, prefix = "R$")
) %>%
# Seleciona apenas as colunas finais para a exibição
select(Ativo, `Quantidade / Fração`, `Preço Unitário (R$)`, `Valor a Investir (R$)`)
print(as.data.frame(lista_de_compras))
## Ativo Quantidade / Fração Preço Unitário (R$) Valor a Investir (R$)
## 1 BTC 0.06570253 608804.53 R$40,000.00
## 2 BPAC11 614 48.85 R$29,993.90
## 3 IVVB11 25 398 R$9,950.00
## 4 EQTL3 270 37.01 R$9,992.70
## 5 CDI R$10,063.40 <NA> R$10,063.40
O ShinyLive não consegue acessar APIs por motivos de segurança, geraremos os arquivos RDS com os dados necessários para construir o portfolio do investidor “on the fly”.
saveRDS(pesos_otimizados, file = "pesos_otimizados.rds")
saveRDS(precos_atuais_df, file = "precos_atuais.rds")